package org.example;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.VarHandle;
import java.util.concurrent.locks.LockSupport;

/**
 * MethodHandle为Java增加了方法引用的功能，方法引用的概念有点类似于C的函数指针。
 * 这种方法引用是一种轻量级的引用方式，它不会检查方法的访问权限，也不管方法所属的类、实例方法或静态方法。
 * MethodHandle就是简单代表特定的方法，并可通过MethodHandle来调用方法。
 * MethodHandles是MethodHandle的工厂类，它提供了一系列静态方法用于获取MethodHandle。
 * MethodHandles.Lookup静态内部类也是MethodHandle、VarHandle的工厂类，专门用于获取MethodHandle和VarHandle。
 * MethodType代表一个方法类型，根据方法的形参、返回值类型来确定方法类型。
 * <p>
 * VarHandle主要用于动态操作数组的元素或对象的成员变量。VarHandle与MethodHandle非常类似，它也需要通过MethodHandles来获取实例。并有CAS操作！
 * <p>
 * 从性能角度上说，MethodHandle api要比反射快很多因为访问检查在创建的时候就已经完成了，而不是像反射一样等到运行时候才检查。
 * 但Method Handles比反射更难用，因为没有列举类中成员，获取属性访问标志之类的机制。
 * 反射更通用，但是安全性更差，因为可以在不授权的情况下使用反射对象。
 * Reflection API的设计目标是只为Java语言服务的，而MethodHandle则设计为可服务于所有Java虚拟机之上的语言，其中也包括了Java语言
 */
public class MethodHandlesDemo {
    private volatile int state = 0;
    private static final VarHandle STATE;

    static {
        try {
            //创建一个Lookup对象，该对象可以创建所有访问权限的方法，包括public，protected，private，default。
            MethodHandles.Lookup l = MethodHandles.lookup();
            STATE = l.findVarHandle(MethodHandlesDemo.class, "state", int.class);
        } catch (ReflectiveOperationException e) {
            throw new ExceptionInInitializerError(e);
        }

        // Reduce the risk of rare disastrous classloading in first call to
        // LockSupport.park: https://bugs.openjdk.java.net/browse/JDK-8074773
        Class<?> ensureLoaded = LockSupport.class;
    }

    public final boolean compareAndSetState(int expect, int update) {
        return STATE.compareAndSet(this, expect, update);
    }

    private void saySomething() {
        System.out.println("say something");
    }

    private static void hello() {
        System.out.println("hello world");
    }

    public int getState() {
        return state;
    }

    public static void main(String[] args) throws Throwable {
        MethodHandlesDemo methodHandlesDemo = new MethodHandlesDemo();
        System.out.println(methodHandlesDemo.getState());
        methodHandlesDemo.compareAndSetState(0, 2);
        System.out.println(methodHandlesDemo.getState());

        MethodType methodType = MethodType.methodType(void.class);
        MethodHandle staticHello = MethodHandles.lookup().findStatic(MethodHandlesDemo.class, "hello", methodType);
        staticHello.invoke();

        MethodHandle commonSaySomething = MethodHandles.lookup().findVirtual(MethodHandlesDemo.class, "saySomething", methodType);
        commonSaySomething.invoke(methodHandlesDemo);
    }
}
